package top.jiangqiang.qianyi.generator;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.generator.model.AnnotationAttributes;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.map.Dict;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.SystemUtil;
import org.dromara.hutool.setting.yaml.YamlUtil;
import org.jetbrains.annotations.NotNull;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.springframework.core.convert.converter.Converter;
import top.jiangqiang.qianyi.business.mapstruct.config.MapperSpringConfig;
import top.jiangqiang.qianyi.common.base.model.entity.BaseEntityResponse;
import top.jiangqiang.qianyi.common.base.model.request.BasePageRequest;
import top.jiangqiang.qianyi.data.model.entity.AdminUser;
import top.jiangqiang.qianyi.generator.model.CustomFileConfig;
import top.jiangqiang.qianyi.generator.model.EntityFileConfig;

import java.io.Serial;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 通用代码生成器封装
 *
 * @Date 2024/1/11 14:58
 * @Author Jiangqiang
 */
@Builder
@Data
@Value
public class EnhanceCodeGenerator {

    public static void main(String[] args) {

        List<String> excludeTables = CodeGenerator.listTableNames(AdminUser.class.getPackageName());
        List<String> includeTables = List.of(
//                "qy_sys_log",
//                "qy_admin_user",
//                "qy_user_info",
                "qy_sys_file");

        CodeGenerator codeGenerator = CodeGenerator.builder()
                .author("JiangQiang")
                .includeTables(includeTables)
                .tablePrefix(List.of("qy_"))
//                .excludeTables(excludeTables)
                .entityFileConfig(EntityFileConfig.builder()
                        .filePath("D:\\Documents\\IdeaProjects\\QianYi\\qianyi-code-generator\\src\\main\\java\\top\\jiangqiang\\qianyi\\generator\\t1\\mode")
                        .generateFile(true)
                        .build())
                .customFileConfigs(List.of(
                        getRequestFileConfig(),
                        getPageRequestFileConfig(),
                        getResponseFileConfig(),
                        getRequestToEntityFileConfig(),
                        getEntityToResponseFileConfig()))
                .build();
        Result result = getResult();

        codeGenerator.execute(result.url(), result.username(), result.password());
    }


    private static CustomFileConfig getRequestFileConfig() {
        List<AnnotationAttributes> attributesArrayList = List.of(
                new AnnotationAttributes(Data.class, "@Data")
        );
        return CustomFileConfig.builder()
                .formatNameFunction(tableInfo -> {
                    return "%sRequest.java".formatted(tableInfo.getEntityName());
                })
                .namespace("entityRequest")
                .filePath("D:\\Documents\\IdeaProjects\\QianYi\\qianyi-code-generator\\src\\main\\java\\top\\jiangqiang\\qianyi\\generator\\t1\\req")
                .templatePath("templates/model.java.ftl")
                .extParamsFunction((extParams, tableInfo) -> {
                    List<String> list = tableInfo.getFields()
                            .stream()
                            .flatMap(tableField -> tableField.getAnnotationAttributesList().stream())
                            .flatMap(annotationAttributes -> annotationAttributes.getImportPackages().stream())
                            .filter(s -> !TableId.class.getName().equals(s))
                            .distinct()
                            .toList();

                    List<String> packageStrList = new ArrayList<>(list);
                    List<String> typeAnnotations = new ArrayList<>();
                    for (AnnotationAttributes attributes : attributesArrayList) {
                        packageStrList.addAll(attributes.getImportPackages());
                        typeAnnotations.add(attributes.getDisplayName());

                    }
                    Class<?> superClass = (Class<?>) extParams.get("superClass");
                    if (superClass != null) {
                        packageStrList.add(superClass.getName());
                    } else {
                        packageStrList.add(Serializable.class.getName());
                    }
                    packageStrList.add(Serial.class.getName());
                    packageStrList = packageStrList.stream().sorted().distinct().toList();
                    typeAnnotations = typeAnnotations.stream().sorted().distinct().toList();
                    extParams.put("package", packageStrList);
                    extParams.put("typeAnnotations", typeAnnotations);
                })
                .fileOverride(true)
                .build();
    }


    private static CustomFileConfig getPageRequestFileConfig() {
        List<AnnotationAttributes> attributesArrayList = List.of(
                new AnnotationAttributes(EqualsAndHashCode.class, "@EqualsAndHashCode(callSuper = true)"),
                new AnnotationAttributes(Data.class, "@Data")
        );
        return CustomFileConfig.builder()
                .formatNameFunction(tableInfo -> {
                    return "%sPageRequest.java".formatted(tableInfo.getEntityName());
                })
                .namespace("pageRequest")
                .superClass(BasePageRequest.class)
                .filePath("D:\\Documents\\IdeaProjects\\QianYi\\qianyi-code-generator\\src\\main\\java\\top\\jiangqiang\\qianyi\\generator\\t1\\req")
                .templatePath("templates/model.java.ftl")
                .extParamsFunction((extParams, tableInfo) -> {
                    List<String> list = tableInfo.getFields()
                            .stream()
                            .flatMap(tableField -> tableField.getAnnotationAttributesList().stream())
                            .flatMap(annotationAttributes -> annotationAttributes.getImportPackages().stream())
                            .filter(s -> !TableId.class.getName().equals(s))
                            .distinct()
                            .toList();

                    List<String> packageStrList = new ArrayList<>(list);
                    List<String> typeAnnotations = new ArrayList<>();
                    for (AnnotationAttributes attributes : attributesArrayList) {
                        packageStrList.addAll(attributes.getImportPackages());
                        typeAnnotations.add(attributes.getDisplayName());

                    }
                    Class<?> superClass = (Class<?>) extParams.get("superClass");
                    if (superClass != null) {
                        packageStrList.add(superClass.getName());
                    } else {
                        packageStrList.add(Serializable.class.getName());
                    }
                    packageStrList.add(Serial.class.getName());
                    packageStrList = packageStrList.stream().sorted().distinct().toList();
                    typeAnnotations = typeAnnotations.stream().sorted().distinct().toList();
                    extParams.put("package", packageStrList);
                    extParams.put("typeAnnotations", typeAnnotations);
                })
                .fileOverride(true)
                .build();
    }

    private static CustomFileConfig getResponseFileConfig() {
        List<AnnotationAttributes> attributesArrayList = List.of(
                new AnnotationAttributes(EqualsAndHashCode.class, "@EqualsAndHashCode(callSuper = true)"),
                new AnnotationAttributes(Data.class, "@Data")
        );
        return CustomFileConfig.builder()
                .formatNameFunction(tableInfo -> {
                    return "%sResponse.java".formatted(tableInfo.getEntityName());
                })
                .namespace("entityResponse")
                .filePath("D:\\Documents\\IdeaProjects\\QianYi\\qianyi-code-generator\\src\\main\\java\\top\\jiangqiang\\qianyi\\generator\\t1\\req")
                .templatePath("templates/model.java.ftl")
                .superClass(BaseEntityResponse.class)
                .extParamsFunction((extParams, tableInfo) -> {
                    List<String> list = tableInfo.getFields()
                            .stream()
                            .flatMap(tableField -> tableField.getAnnotationAttributesList().stream())
                            .flatMap(annotationAttributes -> annotationAttributes.getImportPackages().stream())
                            .filter(s -> !TableId.class.getName().equals(s))
                            .distinct()
                            .toList();

                    List<String> packageStrList = new ArrayList<>(list);
                    List<String> typeAnnotations = new ArrayList<>();
                    for (AnnotationAttributes attributes : attributesArrayList) {
                        packageStrList.addAll(attributes.getImportPackages());
                        typeAnnotations.add(attributes.getDisplayName());

                    }
                    Class<?> superClass = (Class<?>) extParams.get("superClass");
                    if (superClass != null) {
                        packageStrList.add(superClass.getName());
                    } else {
                        packageStrList.add(Serializable.class.getName());
                    }
                    packageStrList.add(Serial.class.getName());
                    packageStrList = packageStrList.stream().sorted().distinct().toList();
                    typeAnnotations = typeAnnotations.stream().sorted().distinct().toList();
                    extParams.put("package", packageStrList);
                    extParams.put("typeAnnotations", typeAnnotations);
                })
                .fileOverride(true)
                .build();
    }

    private static CustomFileConfig getRequestToEntityFileConfig() {
        return CustomFileConfig.builder()
                .formatNameFunction(tableInfo -> {
                    return "%sRequestTo%s.java".formatted(tableInfo.getEntityName(), tableInfo.getEntityName());
                })
                .namespace("requestToEntity")
                .filePath("D:\\Documents\\IdeaProjects\\QianYi\\qianyi-code-generator\\src\\main\\java\\top\\jiangqiang\\qianyi\\generator\\t1\\mapstruct")
                .templatePath("templates/mapstruct.java.ftl")
                .extParamsFunction((extParams, tableInfo) -> {
                    Map<?, ?> entityRequestExtParam = (Map<?, ?>) ((Map<?, ?>) extParams.get("files")).get("entityRequest");
                    List<String> packageStrList = new ArrayList<>();
                    Map<?, ?> files = (Map<?, ?>) extParams.get("files");
                    Map<?, ?> entityMap = (Map<?, ?>) files.get("Entity");
                    packageStrList.add(entityMap.get("package") + "." + entityMap.get("simpleName"));
                    packageStrList.add(entityRequestExtParam.get("package") + "." + entityRequestExtParam.get("simpleName"));
                    packageStrList.add(Mapper.class.getName());
                    packageStrList.add(ReportingPolicy.class.getName());
                    packageStrList.add(Converter.class.getName());
                    packageStrList.add(MapperSpringConfig.class.getName());
                    packageStrList = packageStrList.stream().sorted().distinct().toList();
                    extParams.put("package", packageStrList);
                    extParams.put("sourceSimpleName", entityRequestExtParam.get("simpleName"));
                    extParams.put("targetSimpleName", entityMap.get("simpleName"));
                })
                .fileOverride(true)
                .build();
    }

    private static CustomFileConfig getEntityToResponseFileConfig() {
        return CustomFileConfig.builder()
                .formatNameFunction(tableInfo -> {
                    return "%sTo%sResponse.java".formatted(tableInfo.getEntityName(), tableInfo.getEntityName());
                })
                .namespace("requestToEntity")
                .filePath("D:\\Documents\\IdeaProjects\\QianYi\\qianyi-code-generator\\src\\main\\java\\top\\jiangqiang\\qianyi\\generator\\t1\\mapstruct")
                .templatePath("templates/mapstruct.java.ftl")
                .extParamsFunction((extParams, tableInfo) -> {
                    Map<?, ?> entityRequestExtParam = (Map<?, ?>) ((Map<?, ?>) extParams.get("files")).get("entityResponse");
                    List<String> packageStrList = new ArrayList<>();
                    Map<?, ?> files = (Map<?, ?>) extParams.get("files");
                    Map<?, ?> entityMap = (Map<?, ?>) files.get("Entity");
                    packageStrList.add(entityMap.get("package") + "." + entityMap.get("simpleName"));
                    packageStrList.add(entityRequestExtParam.get("package") + "." + entityRequestExtParam.get("simpleName"));
                    packageStrList.add(Mapper.class.getName());
                    packageStrList.add(ReportingPolicy.class.getName());
                    packageStrList.add(Converter.class.getName());
                    packageStrList.add(MapperSpringConfig.class.getName());
                    packageStrList = packageStrList.stream().sorted().distinct().toList();
                    extParams.put("package", packageStrList);
                    extParams.put("sourceSimpleName", entityMap.get("simpleName"));
                    extParams.put("targetSimpleName", entityRequestExtParam.get("simpleName"));
                })
                .fileOverride(true)
                .build();
    }

    private static @NotNull Result getResult() {
        String currentDir = SystemUtil.get("user.dir");
        String configDir = currentDir + "/qianyi-web/src/main/resources/";
        Dict config = YamlUtil.loadByPath(configDir + "application.yaml");
        String active = config.getByPath("spring.profiles.active", String.class);
        String activeYamlFile = configDir + "application-" + active + ".yaml";
        String url = null;
        String username = null;
        String password = null;
        if (FileUtil.exists(activeYamlFile)) {
            Dict activeConfig = YamlUtil.loadByPath(activeYamlFile);
            url = activeConfig.getByPath("spring.datasource.url");
            username = activeConfig.getByPath("spring.datasource.username");
            password = activeConfig.getByPath("spring.datasource.password").toString();
        }
        if (StrUtil.isBlank(url)) {
            url = config.getByPath("spring.datasource.url");
        }
        if (StrUtil.isBlank(username)) {
            username = config.getByPath("spring.datasource.username");
        }
        if (StrUtil.isBlank(password)) {
            password = config.getByPath("spring.datasource.password").toString();
        }
        Result result = new Result(url, username, password);
        return result;
    }

    private record Result(String url, String username, String password) {
    }

}
